using UnityEngine;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Net;

public class Galaxy : ISerializable
{
	public static int PlayerGalaxySize = (int)GalaxySize.Huge;
	public static GameLevel PlayerGameLevel = GameLevel.Easy;
	public static int StarSerial = 0;
	public static ulong FleetSerial = 0;
	public static int FacilitySerial = 0;

	public static int NumOfKingdom = 6;
	public static int ScreenRes = 0;


	#region enums
	public enum GalaxySize
	{
		Tiny = 2000,
		Small = 3000,
		Medium = 4000,
		Large = 5000,
		Huge = 6000
	}

	public enum GameLevel
	{
		Easy,
		Medium,
		Hard,
		Impossible
	}

	public enum Civilization
	{
		Gifted = 0,
		Primitive = 1,
		Medieval = 2,
		GreatLeap = 3,
		Modern = 4,
		Advanced = 5,
		SuperModern = 6,
		FarBeyond = 7,
	}

	public enum RaceType
	{
		None = 0,
		Human = 1,
		Elf = 2,
		Gnome = 3,
		Dwarf = 4,
		Orc = 5,
		Halfling = 6,
		Pixie = 7,
		DarkElf = 8,
	}

	public enum Profession
	{
		Paladin,
		Warrior,
		Priest,
		Bard,
		Mage,
		Rogue
	}
	#endregion


	// Declaration
	private int m_StarSystem;					// number of star systems in galaxy

	private List<StarSystem> m_Solars = new List<StarSystem>();
	private List<Point3D> m_Locations = new List<Point3D>();
	private List<Point3D> m_Homes = new List<Point3D>();


	// ISerializable
	ulong ISerializable.Serial
	{
		get { return (ulong)1; }
	}


	#region getters and setters
	public int StarSystem
	{
		get { return m_StarSystem; }
		set { m_StarSystem = value; }
	}

	public List<StarSystem> Solars
	{
	  get { return m_Solars; }
	  set { m_Solars = value; }
	}

	public List<Point3D> Locations
	{
			get { return m_Locations; }
	}

	public List<Point3D> Homes
	{
		get { return m_Homes; }
		set { m_Homes = value; }
	}
	#endregion


	// default constructor of Galaxy
	public Galaxy()
	{
		m_StarSystem = PlayerGalaxySize / 100;
	}


	// sub classes
	public class SolarSorterZero : IComparer<StarSystem>
	{
		public int Compare(StarSystem x, StarSystem y)
		{
			Point3D xpt = x.Location;
			Point3D ypt = y.Location;

			double xdist = Math.Sqrt(Math.Pow(xpt.X, 2) + Math.Pow(xpt.Y, 2) + Math.Pow(xpt.Z, 2));
			double ydist = Math.Sqrt(Math.Pow(ypt.X, 2) + Math.Pow(ypt.Y, 2) + Math.Pow(ypt.Z, 2));

			return Compare((int)xdist, (int)ydist);
		}

		public int Compare(int x, int y)
		{
			return x - y;
		}
	}

	public class LocSorterZero : IComparer<Point3D>
	{
		public int Compare(Point3D x, Point3D y)
		{
			double xdist = Math.Sqrt(Math.Pow(x.X, 2) + Math.Pow(x.Y, 2) + Math.Pow(x.Z, 2));
			double ydist = Math.Sqrt(Math.Pow(y.X, 2) + Math.Pow(y.Y, 2) + Math.Pow(y.Z, 2));

			return Compare((int)xdist, (int)ydist);
		}

		public int Compare(int x, int y)
		{
			return x - y;
		}
	}


	// ISerializable
	public virtual void Serialize(GenericWriter writer)
	{
		writer.Write((int)0); // version

		writer.Write((int)PlayerGalaxySize);
		writer.Write((int)PlayerGameLevel);
		writer.Write((int)StarSerial);
		writer.Write((ulong)FleetSerial);
		writer.Write((int)FacilitySerial);

		writer.Write((int)NumOfKingdom);
		writer.Write((int)ScreenRes);
		writer.Write((int)m_StarSystem);

		writer.Write((int)m_Solars.Count);
		foreach (StarSystem ss in m_Solars)
			writer.Write((StarSystem)ss);

		writer.Write((int)m_Locations.Count);
		foreach (Point3D pt in m_Locations)
			writer.Write((Point3D)pt);

		writer.Write((int)m_Homes.Count);
		foreach (Point3D home in m_Homes)
			writer.Write((Point3D)home);

	}

	public virtual void Deserialize(GenericReader reader)
	{
		int version = reader.ReadInt();

		PlayerGalaxySize = reader.ReadInt();
		PlayerGameLevel = (GameLevel)reader.ReadInt();
		StarSerial = reader.ReadInt();
		FleetSerial = reader.ReadULong();
		FacilitySerial = reader.ReadInt();

		NumOfKingdom = reader.ReadInt();
		ScreenRes = reader.ReadInt();
		m_StarSystem = reader.ReadInt();

		int count = reader.ReadInt();
		m_Solars = new List<StarSystem>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				m_Solars.Add(reader.ReadStarSystem());
				m_Solars.Sort(new SolarSorterZero());
			}

		count = reader.ReadInt();
		m_Locations = new List<Point3D>();

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				m_Locations.Add(reader.ReadPoint3D());
				m_Locations.Sort(new LocSorterZero());
			}

		count = reader.ReadInt();
		m_Homes = new List<Point3D>();

		if (count > 0)
			for (int i = 0; i < count; i++)
				m_Homes.Add(reader.ReadPoint3D());

	}


	// class methods
	public bool InitializeStars(int num)
	{
		bool ok = AddStarLocations(num);

		if (!ok)
			throw new ArgumentException();

		bool ishome = false;

		foreach (Point3D pt in m_Locations)
		{
			foreach (Point3D home in m_Homes)
			{
				if (home == pt)
					ishome = true;
			}

			StarSystem ss = new StarSystem(pt, ishome);

			m_Solars.Add(ss);
			m_Solars.Sort(new SolarSorterZero());

			Core.Stars.Add(ss.StarID, ss);
		}

		return true;
	}

	public void OnTurn()
	{
		Core.TotalTurn++;
		Core.oGuiCam.GetComponent<GuiCamera>().inTurn = true;

		foreach (Kingdom kd in Core.galaxyKingdoms)
		{
			kd.OnBeginTurn();
			kd.OnTurn();
			kd.OnEndTurn();
		}

		// for monitoring output
		StatusOut.SendKingdomsOut();

		Core.oGuiCam.GetComponent<GuiCamera>().inTurn = false;
	}

	public bool AddStarLocations(int num)
	{
		int size = PlayerGalaxySize;
		int count = 0;
		bool found;
		double xydist;
		double distance;
		Point3D pt;

		do
		{
			pt = Utility.GetRandomPoint3D(size, size, size);
			found = false;

			foreach (Point3D p3d in m_Locations)
			{
				xydist = Math.Sqrt(Math.Pow(p3d.X - pt.X, 2) + Math.Pow(p3d.Y - pt.Y, 2));
				distance = Math.Sqrt(Math.Pow(p3d.X - pt.X, 2) + Math.Pow(p3d.Y - pt.Y, 2) + Math.Pow(p3d.Z - pt.Z, 2));

				if (xydist < 120 || distance < 200)
					found = true;
			}

			if (!found)
			{
				m_Locations.Add(pt);
				m_Locations.Sort(new LocSorterZero());
				count++;
			}

		} while (count < num);

		// to generate home stars
		int factor = m_StarSystem / NumOfKingdom;
		int home;

		for (int i = 0; i < NumOfKingdom; i++)
		{
			home = factor * i + factor / 2;

			m_Homes.Add(m_Locations[home]);
		}

		m_Homes = RandomizeHome(m_Homes);

		foreach (Point3D ptt in m_Homes)
			Debug.Log("home after: " + ptt.ToString());

		return true;
	}


	// static methods
	public static Race CreateRandRace()
	{
		Galaxy.RaceType rtype = GetRandKingdomRace();

		return new Race(rtype);
	}

	public static RaceType GetRandKingdomRace()
	{
		if (Core.galaxyKingdoms.Count > Core.maxKingdom)
			return Galaxy.RaceType.None;

		List<Galaxy.RaceType> lrace = new List<Galaxy.RaceType>();
		Galaxy.RaceType race = Galaxy.RaceType.None;
		bool found;

		for (int i = 0; i < 8; i++)						// there are overall 8 races in Galaxy.RaceType enum
		{
			race = (Galaxy.RaceType)(i + 1);
			found = false;

			foreach (Kingdom kd in Core.galaxyKingdoms)
			{
				if (kd.Race.RaceType == race)
					found = true;
			}

			if (!found)
				lrace.Add(race);
		}

		if (lrace.Count == 0)
			return Galaxy.RaceType.None;

		return lrace[Utility.Random(lrace.Count)];
	}

	public static List<Point3D> RandomizeHome(List<Point3D> lhome)
	{
		if (null == lhome || lhome.Count == 0)
			return new List<Point3D>();

		List<Point3D> lpt = new List<Point3D>();
		int rand;
		int count = lhome.Count;

		for (int i = 0; i < count; i++)
		{
			rand = Utility.Random(lhome.Count);
			lpt.Add(lhome[rand]);

			lhome.RemoveAt(rand);
		}

		return lpt;
	}
}
